package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    private CategoryFeignClient categoryFeignClient;
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
    }

    @Override
    public void upperAlbum(Long albumId) {
        //需要远程调用获取数据，并将数据封装到AlbumInfoIndex;
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //异步编排
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //远程调用专辑微服务获取专辑信息
            Result<AlbumInfo> infoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            Assert.notNull(infoResult,"远程调用获取专辑信息失败");
            AlbumInfo albumInfo = infoResult.getData();
            Assert.notNull(albumInfo,"远程调用获取专辑信息失败");
            //属性拷贝
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);
            //取出属性值集合
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            //用stream流方式封装
            List<AttributeValueIndex> list = albumAttributeValueVoList.stream().map(albumAttributeValue -> {
                //创建attributeValueIndex对象
                AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                //属性拷贝
                attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                return attributeValueIndex;
            }).collect(Collectors.toList());
            //封装属性属性值id集合到albumInfoIndex对象
            albumInfoIndex.setAttributeValueIndexList(list);
            return albumInfo;
        },threadPoolExecutor).exceptionally(ex ->{
            System.out.println(ex.getMessage());
            return null;
        });

        CompletableFuture<Void> categoryCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            //根据albuminfo里面三级分类id远程调用专辑微服务获取分类id
            Result<BaseCategoryView> viewResult = categoryFeignClient.getBaseCategoryView(albumInfo.getCategory3Id());
            Assert.notNull(viewResult, "远程调用获取分类视图失败");
            BaseCategoryView baseCategoryView = viewResult.getData();
            Assert.notNull(baseCategoryView,"远程调用获取分类视图失败");
            //封装分类信息albumInfoIndex对象
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
        },threadPoolExecutor).exceptionally(ex->{
            System.out.println(ex.getMessage());
            return null;
        });

        CompletableFuture<Void> userCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            //根据用户id远程调用用户微服务获取用户信息 我们业务是：登录用户就是控制专辑上下架的用户
            Result<UserInfoVo> infoVoResult = userInfoFeignClient.getUserInfo(albumInfo.getUserId());
            Assert.notNull(infoVoResult, "远程调用获取用户信息失败");
            UserInfoVo userInfoVo = infoVoResult.getData();
            Assert.notNull(userInfoVo, "远程调用获取用户信息失败");
            //拷贝主播名称
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        },threadPoolExecutor).exceptionally(ex->{
            System.out.println(ex.getMessage());
            return null;
        });

        CompletableFuture<Void> statCompletableFuture = CompletableFuture.runAsync(() -> {
            //根据专辑id远程调用专辑微服务获取统计信息
            Result<AlbumStatVo> statVoResult = albumInfoFeignClient.getAlbumStatVo(albumId);
            Assert.notNull(statVoResult, "远程调用获取统计信息失败");
            AlbumStatVo albumStatVo = statVoResult.getData();
            Assert.notNull(albumStatVo,"远程调用获取统计信息失败");
            //拷贝统计信息
            BeanUtils.copyProperties(albumStatVo, albumInfoIndex);
            //设置热度值
            albumInfoIndex.setHotScore(new Random().nextDouble(10000));
        },threadPoolExecutor).exceptionally(ex->{
            System.out.println(ex.getMessage());
            return null;
        });

        //通常get方法只调用一次应该将方法放在最后

        //多任务组合
        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                categoryCompletableFuture,
                userCompletableFuture,
                statCompletableFuture
        ).join();

        //将albumInfoIndex保存到es
        albumInfoIndexRepository.save(albumInfoIndex);
        //保存提词库
        //创建提词对象
        SuggestIndex suggestIndexTitle = new SuggestIndex();
        //赋值专辑标题
        suggestIndexTitle.setId(UUID.randomUUID().toString().replaceAll("-",""));
        suggestIndexTitle.setTitle(albumInfoIndex.getAlbumTitle());
        //将整个标题当成一个数组元素
        suggestIndexTitle.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        suggestIndexTitle.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumTitle())}));
        suggestIndexTitle.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle())}));
        suggestIndexRepository.save(suggestIndexTitle);
        //赋值专辑简介
        SuggestIndex suggestIndexIntro = new SuggestIndex();
        suggestIndexIntro.setId(UUID.randomUUID().toString().replaceAll("-",""));
        suggestIndexIntro.setTitle(albumInfoIndex.getAlbumIntro());
        suggestIndexIntro.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumIntro()}));
        suggestIndexIntro.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumIntro())}));
        suggestIndexIntro.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumIntro())}));
        suggestIndexRepository.save(suggestIndexIntro);
        //赋值作者
        SuggestIndex suggestIndexUser = new SuggestIndex();
        suggestIndexUser.setId(UUID.randomUUID().toString().replaceAll("-",""));
        suggestIndexUser.setTitle(albumInfoIndex.getAnnouncerName());
        suggestIndexUser.setKeyword(new Completion(new String[]{albumInfoIndex.getAnnouncerName()}));
        suggestIndexUser.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAnnouncerName())}));
        suggestIndexUser.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAnnouncerName())}));
        suggestIndexRepository.save(suggestIndexUser);
    }

    /**
     * @author gengjian
     * @date 2025/02/26 10:40:29
     * 关键词或分类检索
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        //生成dsl语句
        //为了业务代码清晰我们封装dsl语句方法
        SearchRequest searchRequest = this.queryBuildDsl(albumIndexQuery);
        SearchResponse<AlbumInfoIndex> response;
        //执行dsl语句
        try {
            response = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //进行数据类型转换 只给list赋值了
        AlbumSearchResponseVo albumSearchResponseVo = this.parseResultData(response);
        //给其他属性赋值 总页数=记录总数/每页记录数 除不尽+1  我们采用另一种写法
        albumSearchResponseVo.setTotal(response.hits().total().value());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
//        albumSearchResponseVo.setTotalPages(
//                albumSearchResponseVo.getTotal() % albumIndexQuery.getPageSize() == 0 ?
//                albumSearchResponseVo.getTotal() / albumIndexQuery.getPageSize() :
//                albumSearchResponseVo.getTotal() / albumIndexQuery.getPageSize() + 1);
        albumSearchResponseVo.setTotalPages(
                (albumSearchResponseVo.getTotal()+albumIndexQuery.getPageSize()-1) /
                        albumIndexQuery.getPageSize());
        //返回结果
        return albumSearchResponseVo;
    }

    /**
     * @author gengjian
     * @date 2025/03/02 01:52:05
     * 数据类型转换
     */
    private AlbumSearchResponseVo parseResultData(SearchResponse<AlbumInfoIndex> response) {
        //我们要从es结果中取出数据封装到AlbumSearchResponseVo中
        //创建对象
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        //遍历response
        List<AlbumInfoIndexVo> list = response.hits().hits().stream().map(m -> {
            AlbumInfoIndex albumInfoIndex = m.source();
            //封装AlbumInfoIndexVo对象
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            //替换标题为高亮结果 如果集合不空则有高亮
            if (!CollectionUtils.isEmpty(m.highlight().get("albumTitle"))){
                String albumTitle = m.highlight().get("albumTitle").get(0);
                albumInfoIndexVo.setAlbumTitle(albumTitle);
            }
            return albumInfoIndexVo;
        }).collect(Collectors.toList());
        //封装list集合到对象中
        albumSearchResponseVo.setList(list);
        return albumSearchResponseVo;
    }

    /**
     * @author gengjian
     * @date 2025/03/01 22:45:50
     * 生成dsl语句
     */
    private SearchRequest queryBuildDsl(AlbumIndexQuery albumIndexQuery) {
        //通过builder创建对象
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        searchRequestBuilder.index("albuminfo");
        //query对象->bool对象
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        //如果关键词不为空 则根据关键词查 有可能是作者、专辑简介、专辑标题
        if (!StringUtils.isEmpty(albumIndexQuery.getKeyword())){
            boolQueryBuilder
                    .should(s->s.match(m->m.field("albumTitle").query(albumIndexQuery.getKeyword())))
                    .should(s->s.match(m->m.field("albumIntro").query(albumIndexQuery.getKeyword())))
                    .should(s->s.match(m->m.field("announcerName").query(albumIndexQuery.getKeyword())));
            //设置高亮 只设置标题即可
            searchRequestBuilder
                .highlight(h->h.fields("albumTitle", f->f
                    .preTags("<span style=color:red>")
                    .postTags("</span>")));
        }

        //检索的第二个入口，如果分类不为空则安分类查
        if (albumIndexQuery.getCategory1Id() != null){
            boolQueryBuilder.filter(f->f.term(t->t.field("category1Id").value(albumIndexQuery.getCategory1Id())));
        }
        if (albumIndexQuery.getCategory2Id() != null){
            boolQueryBuilder.filter(f->f.term(t->t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        if (albumIndexQuery.getCategory3Id() != null){
            boolQueryBuilder.filter(f->f.term(t->t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }

        //检索第三个入口 追加的属性和属性值过滤
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (!CollectionUtils.isEmpty(attributeList)){
            //遍历将属性id：属性值id分割
            for (String s : attributeList) {
                String[] split = s.split(":");
                if (!ObjectUtils.isEmpty(split) && split.length == 2){
                    boolQueryBuilder.filter(f->f.nested(n->n.path("attributeValueIndexList")
                        .query(q->q.bool(b->b
                            .must(m->m.match(m1->m1.field("attributeValueIndexList.attributeId").query(split[0])))
                            .must(m->m.match(m1->m1.field("attributeValueIndexList.valueId").query(split[1])))))));
                }
            }
        }
        //排序 综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]；asc:升序 desc:降序
        String order = albumIndexQuery.getOrder();
        if (!StringUtils.isEmpty(order)){
            String[] split = order.split(":");
            String field = "";
            switch (split[0]){
                case "1":
                    field = "hotScore";
                    break;
                case "2":
                    field = "playStatNum";
                    break;
                case "3":
                    field = "createTime";
                    break;
            }
            String finalField = field;
            searchRequestBuilder
                    .sort(s->s.field(f->f.field(finalField)
                            .order("asc".equals(split[1]) ? SortOrder.Asc : SortOrder.Desc)));
        }
        //分页
        int from = (albumIndexQuery.getPageNo()-1)*albumIndexQuery.getPageSize();
        searchRequestBuilder.from(from);
        searchRequestBuilder.size(albumIndexQuery.getPageSize());

        BoolQuery boolQuery = boolQueryBuilder.build();
        searchRequestBuilder.query(boolQuery._toQuery());
        SearchRequest searchRequest = searchRequestBuilder.build();

        return searchRequest;
    }

    /**
     * @author gengjian
     * @date 2025/03/02 15:35:49
     * 根据一级分类id查频道列表数据
     */
    @Override
    public List<Map<String, Object>> channel(Long category1Id) {
        //根据前台页面我们封装成list<map>结构
        //需要三级分类id列表
        Result<List<BaseCategory3>> listResult = categoryFeignClient.findTopBaseCategory3(category1Id);
        Assert.notNull(listResult,"远程调用分类数据失败");
        List<BaseCategory3> category3List = listResult.getData();
        Assert.notNull(category3List,"远程调用分类数据失败");
        List<Long> category3IdList = category3List.stream().map(BaseCategory3::getId).toList();
        //将list集合转成map集合 方便后面构建map.put获取三级分类对象
        Map<Long, BaseCategory3> category3Map =
                category3List.stream().collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));
        //编写dsl语句查询根据三级分类id分好组的专辑列表
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        searchRequestBuilder.index("albuminfo");
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        //需要list<FieldValue> 而我们是 List<Long> 需要转换
        List<FieldValue> list = category3IdList.stream().map(category3Id -> FieldValue.of(category3Id)).toList();
        //先是in查询
        boolQueryBuilder.filter(f->f.terms(t->t.field("category3Id").terms(t1->t1.value(list))));
        //再是分组和根据热度倒排序
        searchRequestBuilder.aggregations("aggs_category3Id",a->a.terms(t->t
                .field("category3Id"))
                .aggregations("aggs_category3AlbumInfo",a1->a1.topHits(t->t
                        .size(6)
                        .sort(s->s.field(f->f.field("hotScore").order(SortOrder.Desc))))));

        BoolQuery boolQuery = boolQueryBuilder.build();
        searchRequestBuilder.query(boolQuery._toQuery());
        SearchRequest searchRequest = searchRequestBuilder.build();

        SearchResponse<AlbumInfoIndex> response;
        //执行dsl语句
        try {
            response = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //从结果中获取aggregations中的buckets是一个list集合
        Aggregate aggregate = response.aggregations().get("aggs_category3Id");
        //构建list<map>集合 我们不需要因为buckets也是一个list集合
//        List<Map<String,Object>> channelList = new ArrayList<>();
        List<Map<String, Object>> channelList = aggregate.lterms().buckets().array().stream().map(longTermsBucket -> {
            //遍历数组得到key=三级分类id
            long category3Id = longTermsBucket.key();
            //构建map结构
            Map<String, Object> map = new HashMap<>();
            //通过三级分类id获取三级分类对象
            map.put("baseCategory3", category3Map.get(category3Id));
            //从es中获取专辑列表
            Aggregate aggregate1 = longTermsBucket.aggregations().get("aggs_category3AlbumInfo");
            List<AlbumInfoIndex> albumInfoIndexList = aggregate1.topHits().hits().hits().stream().map(hit -> {
                //拿到的是jsondata转换成字符串先
                String string = hit.source().toString();
                //再转换为AlbumInfoIndex对象
                AlbumInfoIndex albumInfoIndex = JSONObject.parseObject(string, AlbumInfoIndex.class);
                return albumInfoIndex;
            }).toList();
            map.put("list", albumInfoIndexList);
            return map;
        }).toList();
        //返回结果
        return channelList;
    }

    /**
     * @author gengjian
     * @date 2025/03/01 09:48:26
     * 根据专辑id查询专辑详情
     */
    @Override
    public Map<String, Object> findAlbumDetail(Long albumId) {
        //创建map结构
        Map<String,Object> map = new HashMap<>();
        //远程调用专辑信息
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            Assert.notNull(albumInfoResult, "远程调用专辑信息失败");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "远程调用专辑信息失败");
            map.put("albumInfo", albumInfo);
            return albumInfo;
        }, threadPoolExecutor).exceptionally(ex -> {
            System.out.println(ex.getMessage());
            return new AlbumInfo();
        });
        //远程调用专辑分类信息
        CompletableFuture<Integer> categoryCompletableFuture = albumInfoCompletableFuture.thenApplyAsync(albumInfo -> {
            Result<BaseCategoryView> categoryViewResult = categoryFeignClient.getBaseCategoryView(albumInfo.getCategory3Id());
            Assert.notNull(categoryViewResult, "远程调用专辑分类信息失败");
            BaseCategoryView categoryView = categoryViewResult.getData();
            Assert.notNull(categoryView, "远程调用专辑分类信息失败");
            map.put("baseCategoryView", categoryView);
            //成功返回1
            return 1;
        }, threadPoolExecutor).exceptionally(ex -> {
            System.out.println(ex.getMessage());
            return 0;
        });
        //远程调用专辑统计信息
        CompletableFuture<Integer> statCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Result<AlbumStatVo> statVoResult = albumInfoFeignClient.getAlbumStatVo(albumId);
            Assert.notNull(statVoResult, "远程调用专辑统计信息失败");
            AlbumStatVo albumStatVo = statVoResult.getData();
            Assert.notNull(albumStatVo, "远程调用专辑统计信息失败");
            map.put("albumStatVo", albumStatVo);
            return 1;
        }, threadPoolExecutor).exceptionally(ex -> {
            System.out.println(ex.getMessage());
            return 0;
        });
        //远程调用主播信息
        CompletableFuture<Integer> userCompletableFuture = albumInfoCompletableFuture.thenApplyAsync(albumInfo -> {
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfo(albumInfo.getUserId());
            Assert.notNull(userInfoVoResult, "远程调用主播信息失败");
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            Assert.notNull(userInfoVo, "远程调用主播信息失败");
            map.put("announcer", userInfoVo);
            return 1;
        }, threadPoolExecutor).exceptionally(ex -> {
            System.out.println(ex.getMessage());
            return 0;
        });
        //判断这些任务是否全部成功
        try {
            if (albumInfoCompletableFuture.get() == null || categoryCompletableFuture.get() == 0
                    || userCompletableFuture.get() == 0 || statCompletableFuture.get() == 0){
                //未成功则返回
                return new HashMap<>();
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        //组合任务
        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                statCompletableFuture,
                categoryCompletableFuture,
                userCompletableFuture
        ).join();

        return map;
    }
    /**
     * @author gengjian
     * @date 2025/03/03 15:34:09
     * 自动补全提词
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        //调用es对象查提词库
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        SearchRequest searchRequest = searchRequestBuilder.index("suggestinfo").suggest(s -> s
                .suggesters("suggestionKeyword", s1 -> s1.prefix(keyword)
                        .completion(c -> c.field("keyword")
                                .skipDuplicates(true)
                                .fuzzy(f -> f.fuzziness("auto"))
                                .size(10)))
                .suggesters("suggestionkeywordPinyin", s1 -> s1.prefix(keyword)
                        .completion(c -> c.field("keywordPinyin")
                                .skipDuplicates(true)
                                .fuzzy(f -> f.fuzziness("auto"))
                                .size(10)))
                .suggesters("suggestionkeywordSequence", s1 -> s1.prefix(keyword)
                        .completion(c -> c.field("keywordSequence")
                                .skipDuplicates(true)
                                .fuzzy(f -> f.fuzziness("auto"))
                                .size(10)))).build();
        SearchResponse<SuggestIndex> response;
        try {
             response = elasticsearchClient.search(searchRequest, SuggestIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //获取集合数据 因为要从三个key中取 方式都一样 所以我们封装一个通用方法
        List<String> list1 = getSuggestionTitle(response,"suggestionKeyword");
        List<String> list2 = getSuggestionTitle(response,"suggestionkeywordPinyin");
        List<String> list3 = getSuggestionTitle(response,"suggestionkeywordSequence");

        HashSet<String> hashSet = new HashSet<>();
        hashSet.addAll(list1);
        hashSet.addAll(list2);
        hashSet.addAll(list3);
        return new ArrayList<>(hashSet);
    }

    /**
     * @author gengjian
     * @date 2025/03/03 16:21:02
     * 获取集合数据 因为要从三个key中取 方式都一样 所以我们封装一个通用方法
     */
    private List<String> getSuggestionTitle(SearchResponse<SuggestIndex> response, String key) {
        //获取集合数据 返回title集合
        List<Suggestion<SuggestIndex>> suggestionList = response.suggest().get(key);
        if (!CollectionUtils.isEmpty(suggestionList)){
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : suggestionList) {
                List<String> list = suggestIndexSuggestion.completion().options().stream().map(m -> {
                    String title = m.source().getTitle();
                    return title;
                }).toList();
                return list;
            }
        }
        return new ArrayList<>();
    }
}
